#include "stdlib.h"
#include "gkt_board.h"
#include "gkt_driver.h"
#include "gkt_malloc.h"
#include "gkt_systime.h"
#include "g_i2c.h"
#include "gkt_i2c.h"
#include "gkt_i2c_defines.h"
#include "gkt_gpio.h"

I2C_DEFINE;

#define I2C_THRESHOLD_LEVEL	28
i2c_attr_s gc_i2c_attrs[GKT_I2C_MAXNUMS]= {{GKT_CONFIG_I2C0_SLAVE_NUMS},{GKT_CONFIG_I2C1_SLAVE_NUMS}};

MIIC_Type *i2c_get_type(uint32_t dev_id)
{
	switch (dev_id) 
	{
		case 0:
			return MI2C0;
		case 1:	
			return MI2C1;
		default:
			break;
	}
	return MI2C0;
}

int i2c_active_opnode(i2c_instance_s *instance, 
			i2c_opnode_s *opnode, uint32_t timeout)
{
	int retval=GKT_SUCCESS;

	retval = gkt_semaphore_acquire(instance->active_semaphore, timeout);
	if (GKT_SUCCESS == retval)
		instance->active_opnode_id = (int)opnode->id;
	else
		retval = GKT_EBUSY;

	return retval;
}

void i2c_inactive_opnode(i2c_instance_s *instance)
{
	instance->active_opnode_id = -1;
	gkt_semaphore_release(instance->active_semaphore);	
}

void gkt_i2c_irq_handler(void)
{
	uint32_t irq_num;
	i2c_instance_s *instance;
	MIIC_Type *i2c_type;

	irq_num = gkt_irq_get_current();
	if (irq_num == I2C0_IRQn)
		instance = &g_i2c.instances[0];
	else
		instance = &g_i2c.instances[1];
	i2c_type = i2c_get_type(instance->id);
    //gkt_printf("I2C[%d]_IRQHandler has been called.\n",instance->id);
    
	if (instance->finish_semaphore)
		gkt_semaphore_release(instance->finish_semaphore);
	G_I2C_ClearITPendingBit(i2c_type);
}

void i2c_config_speed(gkt_handle_t handle, uint32_t	speed)
{
	i2c_opnode_s *opnode =(i2c_opnode_s *)handle;
	MIIC_Type *i2c_type;
	i2c_instance_s *instance;
	G_I2C_InitTypeDef I2C_InitStruct;

	instance = opnode->instance;
	i2c_type = i2c_get_type(instance->id);
	switch (opnode->speed) {
		case GKT_I2C_SPEED_STANDARD:
			I2C_InitStruct.scll = SCLL_100KHZ;
			I2C_InitStruct.sclh = SCLH_100KHZ;
			I2C_InitStruct.stsu = STSU_100KHZ;
			I2C_InitStruct.sthd = STHD_100KHZ;
			I2C_InitStruct.sosu = SOSU_100KHZ;
			I2C_InitStruct.dtsu = DTSU_100KHZ;
			I2C_InitStruct.dthd = DTHD_100KHZ;
			break;
		case GKT_I2C_SPEED_FAST:
			I2C_InitStruct.scll = SCLL_400KHZ;
			I2C_InitStruct.sclh = SCLH_400KHZ;
			I2C_InitStruct.stsu = STSU_400KHZ;
			I2C_InitStruct.sthd = STHD_400KHZ;
			I2C_InitStruct.sosu = SOSU_400KHZ;
			I2C_InitStruct.dtsu = DTSU_400KHZ;
			I2C_InitStruct.dthd = DTHD_400KHZ;
			break;
		case GKT_I2C_SPEED_FAST_PLUS:
			I2C_InitStruct.scll = SCLL_800KHZ;
			I2C_InitStruct.sclh = SCLH_800KHZ;
			I2C_InitStruct.stsu = STSU_800KHZ;
			I2C_InitStruct.sthd = STHD_800KHZ;
			I2C_InitStruct.sosu = SOSU_800KHZ;
			I2C_InitStruct.dtsu = DTSU_800KHZ;
			I2C_InitStruct.dthd = DTHD_800KHZ;
			break;
		case GKT_I2C_SPEED_HIGH:
			I2C_InitStruct.scll = SCLL_1_2MHZ;
			I2C_InitStruct.sclh = SCLH_1_2MHZ;
			I2C_InitStruct.stsu = STSU_1_2MHZ;
			I2C_InitStruct.sthd = STHD_1_2MHZ;
			I2C_InitStruct.sosu = SOSU_1_2MHZ;
			I2C_InitStruct.dtsu = DTSU_1_2MHZ;
			I2C_InitStruct.dthd = DTHD_1_2MHZ;
			break;
		default:
			I2C_InitStruct.scll = SCLL_100KHZ;
			I2C_InitStruct.sclh = SCLH_100KHZ;
			I2C_InitStruct.stsu = STSU_100KHZ;
			I2C_InitStruct.sthd = STHD_100KHZ;
			I2C_InitStruct.sosu = SOSU_100KHZ;
			I2C_InitStruct.dtsu = DTSU_100KHZ;
			I2C_InitStruct.dthd = DTHD_100KHZ;
			break;
	}
	
	G_I2C_Init(i2c_type, &I2C_InitStruct);
}

int gkt_i2c_init(void)
{
	i2c_instance_s *instance;
	uint32_t dev_id;
	uint32_t opnode_offset;
	
	gkt_trace("i2c_init: ...\n");

	if (!g_i2c.init) {
		opnode_offset = 0;
		for (dev_id = 0; dev_id < GKT_I2C_MAXNUMS; dev_id++) {
			instance = I2C_INSTANCE(dev_id);
			instance->id = dev_id;
			instance->active_opnode_id = -1;
			instance->send_buf_size_max = 0;
			instance->opnodes = g_i2c_opnodes + opnode_offset;

			
			if (!instance->active_semaphore) {
				instance->active_semaphore = gkt_semaphore_create(1, 1);
				if (!instance->active_semaphore) {
					gkt_error("i2c_init: create active_semaphore failed!\n");
					return GKT_ECREATE;
				}
			}
			
			if (!instance->finish_semaphore) {
				instance->finish_semaphore = gkt_semaphore_create(1, 0);
				if (!instance->finish_semaphore) {
					gkt_error("i2c_init: create semaphore failed!\n");
					return GKT_ECREATE;
				}
			}
			
			opnode_offset += I2C_SLAVE_NUMS(dev_id);
		}

		g_i2c.init = 1;
	}
	
	return GKT_SUCCESS;
}

gkt_handle_t gkt_i2c_open(int master,
			uint32_t dev_id, uint32_t slave_id)
{
	i2c_instance_s *instance;
	i2c_opnode_s *opnode = NULL;

	if (g_i2c.init && (dev_id < GKT_I2C_MAXNUMS)) {
		instance = I2C_INSTANCE(dev_id);
		if (master && (slave_id < I2C_SLAVE_NUMS(dev_id))) {
			opnode = I2C_OPNODE(instance, slave_id);
			if (opnode->state >= I2C_OPNODE_STATE_OPENED) {
				gkt_error("i2c_open: slave(%u - %u) has been opened!\n", dev_id, slave_id);
				return 0;
			}
			opnode->id = (uint8_t)slave_id;
			opnode->state = I2C_OPNODE_STATE_OPENED;
			opnode->instance = instance;
		}
	}

	return (gkt_handle_t)opnode;
}

int gkt_i2c_config(gkt_handle_t handle, 
						gkt_i2c_config_params_s *params)
{
	i2c_instance_s *instance;
	i2c_opnode_s *opnode;
	MIIC_Type *i2c_type;
	int retval;
	gkt_gpio_padmux_desc_s c_desc;
	gkt_irq_attr_s irq_attr;
	uint32_t irq_no;

	if (handle && params) {
		opnode = (i2c_opnode_s *)handle;
		instance = opnode->instance;
		GKT_ASSERT(instance->id == I2C_INSTANCE_DEVID(instance));

		if (I2C_OPNODE_STATE_OPENED == opnode->state) {
			opnode->target_addr = params->target_addr;
			opnode->addr_bits = params->addr_bits;
			opnode->speed = params->speed;
			opnode->restart_enable = params->restart_enable;
			opnode->state = I2C_OPNODE_STATE_CONFIG;

			i2c_type = i2c_get_type(instance->id);
			if (i2c_type == MI2C0) {
				c_desc.pin = GKT_CONFIG_I2C0_SCL_PAD;
				c_desc.func = I2C0_SCL;
				gkt_gpio_padmux_config(&c_desc);
				c_desc.pin = GKT_CONFIG_I2C0_SDA_PAD;
				c_desc.func = I2C0_SDA;
				gkt_gpio_padmux_config(&c_desc);
				//G_GPIO_Config(I2C0SCL_PORT, I2C0SCL_IO_PIN, I2C0_SCL);
				//G_GPIO_Config(I2C0SDA_PORT, I2C0SDA_IO_PIN, I2C0_SDA);
			}
			else if (i2c_type == MI2C1) {
				c_desc.pin = GKT_CONFIG_I2C1_SCL_PAD;
				c_desc.func = I2C1_SCL;
				gkt_gpio_padmux_config(&c_desc);
				c_desc.pin = GKT_CONFIG_I2C1_SDA_PAD;
				c_desc.func = I2C1_SDA;
				gkt_gpio_padmux_config(&c_desc);
				//G_GPIO_Config(I2C1SCL_PORT, I2C1SCL_IO_PIN, I2C1_SCL);
				//G_GPIO_Config(I2C1SDA_PORT, I2C1SDA_IO_PIN, I2C1_SDA);
			}
			i2c_config_speed(handle, params->speed);
			
			//gkt_padmux_config((gkt_padmux_desc_s *)I2C_PADMUX_DESC(instance->id, SCL));
			//gkt_padmux_config((gkt_padmux_desc_s *)I2C_PADMUX_DESC(instance->id, SDA));
			//gkt_irq_enable(I2C_IRQ_NO(instance->id));
			/* setup irq */
			//NVIC_EnableIRQ((instance->id == 0) ? I2C0_IRQn : I2C1_IRQn);
    		//NVIC_SetPriority((instance->id == 0) ? I2C0_IRQn : I2C1_IRQn, 2);
			/* interrupt setup */
			irq_no = (instance->id == 0) ? GKT_I2C0_IRQ_NO : GKT_I2C1_IRQ_NO;
			irq_attr.flags = GKT_IRQ_F_ENABLE | GKT_IRQ_F_UNMASK | GKT_IRQ_F_CLEAR;
			irq_attr.handler = gkt_i2c_irq_handler;
			irq_attr.priority = GKT_IRQ_PRIORITY(GKT_I2C0_IRQ_PRIORITY);
			retval = gkt_irq_setup(irq_no, &irq_attr);
			if (retval) {
				gkt_error("gpio irq install failed - %d\n", retval);
				return retval;
			}
			gkt_irq_enable(irq_no);
			retval = GKT_SUCCESS;
		}
		else
			retval = GKT_ENOTOPEN;
	}
	else
		retval = GKT_EPARAM;
	return retval;
}

int gkt_i2c_close(gkt_handle_t handle)
{
	i2c_opnode_s *opnode =(i2c_opnode_s *)handle;

	if (opnode) 
		opnode->state = I2C_OPNODE_STATE_IDLE;
	
	return GKT_SUCCESS;
}

int gkt_i2c_master_receive(gkt_handle_t handle,
									void *buffer, uint32_t to_receive_size, 
									uint32_t *received_size,uint32_t timeout)
{
	i2c_instance_s *instance;
	i2c_opnode_s *opnode;
	int retval;
	MIIC_Type *i2c_type;
	uint8_t target_addr;
	
	if (handle && buffer && to_receive_size && received_size) {
		opnode = (i2c_opnode_s *)handle;
		if (opnode->state < I2C_OPNODE_STATE_CONFIG)
			return GKT_ENOTCONFIG;

		instance = opnode->instance;
		GKT_ASSERT(instance->id == I2C_INSTANCE_DEVID(instance));
		GKT_ASSERT(opnode->id < I2C_SLAVE_NUMS(instance->id));
		i2c_config_speed(handle, opnode->speed);
		retval = i2c_active_opnode(instance, opnode, timeout);
		if (GKT_SUCCESS == retval) {
			instance->receive_total_size = to_receive_size;
			instance->receive_buffer = buffer;
				
			if (timeout) {
				do {	/* clear semaphore */
					retval = gkt_semaphore_acquire(instance->finish_semaphore, 0);
				} while (GKT_SUCCESS == retval);
				retval = GKT_SUCCESS;
			}

			/* HW operation - READ - START */
			i2c_type = i2c_get_type(instance->id);
			target_addr = (uint8_t)((opnode->target_addr << 1) | 0x1);
			G_I2C_ReceiveBytes(i2c_type, &target_addr, 1, 
					instance->receive_buffer, instance->receive_total_size);
			
			/* HW operation - READ - FINISH */
			if ((GKT_SUCCESS == retval) && timeout && (instance->receive_total_size > I2C_THRESHOLD_LEVEL)) {
				retval = gkt_semaphore_acquire(instance->finish_semaphore, timeout);
				if (GKT_SUCCESS != retval) {
					G_I2C_ITConfig(i2c_type, DISABLE);
					G_I2C_ClearITPendingBit(i2c_type);
				}
			}  
			*received_size = instance->receive_total_size;
		
			i2c_inactive_opnode(instance);
		}
	}
	else
		retval = GKT_EPARAM;

	return retval;
}

static int i2c_alloc_send_buffer(i2c_instance_s *instance,
			uint32_t to_send_size)
{
	uint32_t send_buf_size_max;

	send_buf_size_max = gkt_aligned(to_send_size, 16);
	if (send_buf_size_max > instance->send_buf_size_max) {
		if (instance->send_buffer) {
			gkt_free(instance->send_buffer);
			instance->send_buffer = NULL;
		}
		instance->send_buf_size_max = 0;
	}

	if (!instance->send_buffer) {
		instance->send_buffer = (uint8_t *)gkt_malloc(send_buf_size_max);
		if (!instance->send_buffer) {
			gkt_error("i2c: alloc send_buffer failed.\n");
			return GKT_ENOMEMORY;
		}
		instance->send_buf_size_max = send_buf_size_max;
	}

	return GKT_SUCCESS;
}

#define I2C_TIMEOUT_MIN	50
int gkt_i2c_master_send(gkt_handle_t handle,
				const void *data, uint32_t to_send_size, 
				uint32_t *sent_size, uint32_t timeout)
{
	i2c_instance_s *instance;
	i2c_opnode_s *opnode;
	int retval;
	MIIC_Type *i2c_type;
	
	if (handle && data && to_send_size) {
		if (timeout < I2C_TIMEOUT_MIN)
			timeout = I2C_TIMEOUT_MIN;

		opnode = (i2c_opnode_s *)handle;
		if (opnode->state < I2C_OPNODE_STATE_CONFIG)
			return GKT_ENOTCONFIG;

		instance = opnode->instance;
		GKT_ASSERT(instance->id == I2C_INSTANCE_DEVID(instance));
		GKT_ASSERT(opnode->id < I2C_SLAVE_NUMS(instance->id));

		i2c_config_speed(handle, opnode->speed);
		retval = i2c_active_opnode(instance, opnode, timeout);
		if (GKT_SUCCESS == retval) {
			retval = i2c_alloc_send_buffer(instance, to_send_size + 1);
			if (retval != GKT_SUCCESS) {
				i2c_inactive_opnode(instance);
				return retval;
			}

			/* build send data */
			instance->send_size = to_send_size + 1;///1: slave addr
			instance->send_buffer[0] = (uint8_t)(opnode->target_addr << 1);
			memcpy(&instance->send_buffer[1], data, to_send_size);

			i2c_type = i2c_get_type(instance->id);
			/* HW operation - Write - START */
			if (instance->send_size > I2C_THRESHOLD_LEVEL) {
				/* clear semaphore */
				gkt_semaphore_acquire(instance->finish_semaphore, 0);

				G_I2C_ITConfig(i2c_type, ENABLE);
		 		G_I2C_ClearITPendingBit(i2c_type);
			}

			G_I2C_SendBytes(i2c_type, instance->send_buffer, instance->send_size);
			/* HW operation - Send - FINISH */
			if (instance->send_size > I2C_THRESHOLD_LEVEL) {
				retval = gkt_semaphore_acquire(instance->finish_semaphore, timeout);
				G_I2C_ITConfig(i2c_type, DISABLE);
				G_I2C_ClearITPendingBit(i2c_type);
			}

			i2c_inactive_opnode(instance);

			if (sent_size && (GKT_SUCCESS == retval))
				*sent_size = to_send_size;
		}
	}
	else
		retval = GKT_EPARAM;

	return retval;
}

int gkt_i2c_master_transmit(gkt_handle_t handle,
			const void *send_data, uint32_t to_send_size, uint32_t *sent_size, 
			void *receive_data, uint32_t to_receive_size, uint32_t *received_size, 
			uint32_t timeout)
{
	i2c_instance_s *instance;
	i2c_opnode_s *opnode;
	MIIC_Type *i2c_type;
	int retval;

	if (handle && send_data && to_send_size
		&& receive_data && to_receive_size) 
	{
		if (timeout < I2C_TIMEOUT_MIN)
			timeout = I2C_TIMEOUT_MIN;
	
		opnode = (i2c_opnode_s *)handle;
		if (opnode->state < I2C_OPNODE_STATE_CONFIG)
			return GKT_ENOTCONFIG;

		instance = opnode->instance;
		GKT_ASSERT(instance->id == I2C_INSTANCE_DEVID(instance));
		GKT_ASSERT(opnode->id < I2C_SLAVE_NUMS(instance->id));

		i2c_config_speed(handle, opnode->speed);
		retval = i2c_active_opnode(instance, opnode, timeout);
		if (GKT_SUCCESS == retval) {
			retval = i2c_alloc_send_buffer(instance, to_send_size + 2);
			if (retval != GKT_SUCCESS) {
				i2c_inactive_opnode(instance);
				return retval;
			}

			instance->send_size = to_send_size + 2;//target_addr + read byte
			instance->send_buffer[0] = (uint8_t)(opnode->target_addr << 1);
			memcpy(&instance->send_buffer[1], send_data, to_send_size);
			instance->send_buffer[instance->send_size-1] = (uint8_t)((opnode->target_addr << 1) | 0x1);

			instance->receive_total_size = to_receive_size;
			instance->receive_buffer = receive_data;

			i2c_type = i2c_get_type(instance->id);
			/* HW operation - Write - START */
			if ((instance->send_size > I2C_THRESHOLD_LEVEL)
				|| (instance->receive_total_size > I2C_THRESHOLD_LEVEL)) 
			{
				/* clear semaphore */
				gkt_semaphore_acquire(instance->finish_semaphore, 0);

				G_I2C_ITConfig(i2c_type, ENABLE);
		 		G_I2C_ClearITPendingBit(i2c_type);
			}

			/* HW operation - SEND-READ - START */		
			G_I2C_SendBytes(i2c_type, instance->send_buffer, instance->send_size-1);
			gkt_delay_us(20);
			G_I2C_ReceiveBytes(i2c_type, &instance->send_buffer[instance->send_size-1], 
					1, instance->receive_buffer, instance->receive_total_size);

			/* HW operation - SEND-READ - FINISH */
			if ((instance->send_size > I2C_THRESHOLD_LEVEL)
				|| (instance->receive_total_size > I2C_THRESHOLD_LEVEL)) 
			{
				retval = gkt_semaphore_acquire(instance->finish_semaphore, timeout);
				G_I2C_ITConfig(i2c_type, DISABLE);
				G_I2C_ClearITPendingBit(i2c_type);

				if (GKT_SUCCESS != retval)
					gkt_error("i2c_transmit: wait finish_semaphore failed %d.\n",retval);	
			}
			
			i2c_inactive_opnode(instance);

			if (GKT_SUCCESS == retval) {
				if (received_size)
					*received_size = to_receive_size;
				if (sent_size)
					*sent_size = to_send_size;
			}
		}
	}
	else
		retval = GKT_EPARAM;

	return retval;
}

GKT_DEVICE_DESC_DEFINE(i2c, NORMAL);


